package org.japo.java.binding.samples;

import org.japo.java.binding.converter.NifConverter;
import java.awt.Color;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import org.apache.commons.beanutils.BeanUtils;
import org.japo.java.entities.Matricula;
import org.japo.java.entities.Nif;

/**
 *
 * @author José A. Pacheco Ondoño - joanpaon@gmail.com
 */
public class MatriculaEnlazada extends javax.swing.JFrame {

    // Referencia al modelo
    private Matricula matricula;

    // Fichero de persistencia
    private final String FICHERO = "matricula.xml";

    // Semáforos de validación
    private boolean expedienteOK;
    private boolean nombreOK;
    private boolean apellidosOK;
    private boolean nifOK;

    // Expresiones regulares
    private final String ER_EXPEDIENTE = "[0-9]{1,5}";    // Expediente
    private final String ER_NOMBRE = ".{3,30}";           // Nombre
    private final String ER_APELLIDOS = ".{3,30}";        // Apellidos
    private final String ER_NIF = "[0-9]{8}[TRWAGMYFPDXBNJZSQVHLCKE]"; // NIF

    public MatriculaEnlazada() {
        iniciarAnterior();
        initComponents();
        iniciarPosterior();
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        bindingGroup = new org.jdesktop.beansbinding.BindingGroup();

        btgCurso = new javax.swing.ButtonGroup();
        btgTurno = new javax.swing.ButtonGroup();
        pnlIdentificacion = new javax.swing.JPanel();
        lblExpediente = new javax.swing.JLabel();
        txfExpediente = new javax.swing.JTextField();
        lblNombre = new javax.swing.JLabel();
        txfNombre = new javax.swing.JTextField();
        lblApellidos = new javax.swing.JLabel();
        txfApellidos = new javax.swing.JTextField();
        lblNif = new javax.swing.JLabel();
        txfNif = new javax.swing.JTextField();
        pnlMatricula = new javax.swing.JPanel();
        lblCiclo = new javax.swing.JLabel();
        cbbCiclo = new javax.swing.JComboBox();
        lblCurso = new javax.swing.JLabel();
        rbtCurso1 = new javax.swing.JRadioButton();
        rbtCurso2 = new javax.swing.JRadioButton();
        rbtCurso3 = new javax.swing.JRadioButton();
        lblTurno = new javax.swing.JLabel();
        rbtTurnoM = new javax.swing.JRadioButton();
        rbtTurnoV = new javax.swing.JRadioButton();
        rbtTurnoN = new javax.swing.JRadioButton();
        rbtTurnoD = new javax.swing.JRadioButton();
        lblRepetidor = new javax.swing.JLabel();
        cbxRepetidor = new javax.swing.JCheckBox();
        pnlAlmacenamiento = new javax.swing.JPanel();
        btnCargar = new javax.swing.JButton();
        btnGuardar = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Matriculación");

        pnlIdentificacion.setBorder(javax.swing.BorderFactory.createTitledBorder("Identificación"));

        lblExpediente.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblExpediente.setText("Expediente");

        org.jdesktop.beansbinding.Binding binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${matricula.expediente}"), txfExpediente, org.jdesktop.beansbinding.BeanProperty.create("text_ON_FOCUS_LOST"), "expediente");
        bindingGroup.addBinding(binding);

        txfExpediente.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusLost(java.awt.event.FocusEvent evt) {
                txfExpedienteFocusLost(evt);
            }
            public void focusGained(java.awt.event.FocusEvent evt) {
                txfExpedienteFocusGained(evt);
            }
        });

        lblNombre.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblNombre.setText("Nombre");

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${matricula.nombre}"), txfNombre, org.jdesktop.beansbinding.BeanProperty.create("text_ON_FOCUS_LOST"), "nombre");
        bindingGroup.addBinding(binding);

        txfNombre.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                txfNombreFocusGained(evt);
            }
            public void focusLost(java.awt.event.FocusEvent evt) {
                txfNombreFocusLost(evt);
            }
        });

        lblApellidos.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblApellidos.setText("Apellidos");

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${matricula.apellidos}"), txfApellidos, org.jdesktop.beansbinding.BeanProperty.create("text_ON_FOCUS_LOST"), "apellidos");
        bindingGroup.addBinding(binding);

        txfApellidos.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                txfApellidosFocusGained(evt);
            }
            public void focusLost(java.awt.event.FocusEvent evt) {
                txfApellidosFocusLost(evt);
            }
        });

        lblNif.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblNif.setText("NIF");

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${matricula.nif}"), txfNif, org.jdesktop.beansbinding.BeanProperty.create("text_ON_FOCUS_LOST"), "nif");
        binding.setConverter(new NifConverter());
        bindingGroup.addBinding(binding);

        txfNif.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusLost(java.awt.event.FocusEvent evt) {
                txfNifFocusLost(evt);
            }
            public void focusGained(java.awt.event.FocusEvent evt) {
                txfNifFocusGained(evt);
            }
        });

        javax.swing.GroupLayout pnlIdentificacionLayout = new javax.swing.GroupLayout(pnlIdentificacion);
        pnlIdentificacion.setLayout(pnlIdentificacionLayout);
        pnlIdentificacionLayout.setHorizontalGroup(
            pnlIdentificacionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(pnlIdentificacionLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(pnlIdentificacionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(lblNif, javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(lblApellidos, javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(lblNombre, javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(lblExpediente, javax.swing.GroupLayout.Alignment.TRAILING))
                .addGap(18, 18, 18)
                .addGroup(pnlIdentificacionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(txfNombre)
                    .addComponent(txfExpediente, javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(txfApellidos)
                    .addComponent(txfNif, javax.swing.GroupLayout.DEFAULT_SIZE, 192, Short.MAX_VALUE))
                .addContainerGap())
        );

        pnlIdentificacionLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {lblApellidos, lblExpediente, lblNif, lblNombre});

        pnlIdentificacionLayout.setVerticalGroup(
            pnlIdentificacionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(pnlIdentificacionLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(pnlIdentificacionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(lblExpediente)
                    .addComponent(txfExpediente, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(pnlIdentificacionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(lblNombre)
                    .addComponent(txfNombre, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(pnlIdentificacionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(lblApellidos)
                    .addComponent(txfApellidos, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(pnlIdentificacionLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(lblNif)
                    .addComponent(txfNif, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        pnlIdentificacionLayout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {lblApellidos, lblExpediente, lblNif, lblNombre, txfApellidos, txfExpediente, txfNif, txfNombre});

        pnlMatricula.setBorder(javax.swing.BorderFactory.createTitledBorder("Matrícula"));

        lblCiclo.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblCiclo.setText("Ciclo");

        cbbCiclo.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "ASI", "ASIR", "DAI", "DAM", "DAW", "ESI", "SMR", " " }));

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${matricula.ciclo}"), cbbCiclo, org.jdesktop.beansbinding.BeanProperty.create("selectedItem"), "ciclo");
        bindingGroup.addBinding(binding);

        lblCurso.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblCurso.setText("Curso");

        btgCurso.add(rbtCurso1);
        rbtCurso1.setText("1");

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${matricula.curso1}"), rbtCurso1, org.jdesktop.beansbinding.BeanProperty.create("selected"), "curso1");
        bindingGroup.addBinding(binding);

        btgCurso.add(rbtCurso2);
        rbtCurso2.setText("2");

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${matricula.curso2}"), rbtCurso2, org.jdesktop.beansbinding.BeanProperty.create("selected"), "curso2");
        bindingGroup.addBinding(binding);

        btgCurso.add(rbtCurso3);
        rbtCurso3.setText("3");

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${matricula.curso3}"), rbtCurso3, org.jdesktop.beansbinding.BeanProperty.create("selected"), "curso3");
        bindingGroup.addBinding(binding);

        lblTurno.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblTurno.setText("Turno");

        btgTurno.add(rbtTurnoM);
        rbtTurnoM.setText("M");

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${matricula.turnoM}"), rbtTurnoM, org.jdesktop.beansbinding.BeanProperty.create("selected"), "turnoM");
        bindingGroup.addBinding(binding);

        btgTurno.add(rbtTurnoV);
        rbtTurnoV.setText("V");

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${matricula.turnoV}"), rbtTurnoV, org.jdesktop.beansbinding.BeanProperty.create("selected"), "turnoV");
        bindingGroup.addBinding(binding);

        btgTurno.add(rbtTurnoN);
        rbtTurnoN.setText("N");

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${matricula.turnoN}"), rbtTurnoN, org.jdesktop.beansbinding.BeanProperty.create("selected"), "turnoN");
        bindingGroup.addBinding(binding);

        btgTurno.add(rbtTurnoD);
        rbtTurnoD.setText("D");

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${matricula.turnoD}"), rbtTurnoD, org.jdesktop.beansbinding.BeanProperty.create("selected"), "turnoD");
        bindingGroup.addBinding(binding);

        lblRepetidor.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblRepetidor.setText("Repetidor");

        binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${matricula.repetidor}"), cbxRepetidor, org.jdesktop.beansbinding.BeanProperty.create("selected"), "repetidor");
        bindingGroup.addBinding(binding);

        javax.swing.GroupLayout pnlMatriculaLayout = new javax.swing.GroupLayout(pnlMatricula);
        pnlMatricula.setLayout(pnlMatriculaLayout);
        pnlMatriculaLayout.setHorizontalGroup(
            pnlMatriculaLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(pnlMatriculaLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(pnlMatriculaLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(pnlMatriculaLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                        .addComponent(lblCurso, javax.swing.GroupLayout.DEFAULT_SIZE, 57, Short.MAX_VALUE)
                        .addComponent(lblCiclo, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                    .addComponent(lblTurno, javax.swing.GroupLayout.PREFERRED_SIZE, 57, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(lblRepetidor, javax.swing.GroupLayout.PREFERRED_SIZE, 47, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(pnlMatriculaLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(cbxRepetidor)
                    .addGroup(pnlMatriculaLayout.createSequentialGroup()
                        .addComponent(rbtTurnoM)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(rbtTurnoV)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(rbtTurnoN)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(rbtTurnoD))
                    .addGroup(pnlMatriculaLayout.createSequentialGroup()
                        .addComponent(rbtCurso1)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(rbtCurso2)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(rbtCurso3))
                    .addComponent(cbbCiclo, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addContainerGap(81, Short.MAX_VALUE))
        );

        pnlMatriculaLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {lblCiclo, lblCurso, lblRepetidor, lblTurno});

        pnlMatriculaLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {cbxRepetidor, rbtCurso1, rbtCurso2, rbtCurso3, rbtTurnoD, rbtTurnoM, rbtTurnoN, rbtTurnoV});

        pnlMatriculaLayout.setVerticalGroup(
            pnlMatriculaLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(pnlMatriculaLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(pnlMatriculaLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(lblCiclo)
                    .addComponent(cbbCiclo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(pnlMatriculaLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(lblCurso)
                    .addComponent(rbtCurso1)
                    .addComponent(rbtCurso2)
                    .addComponent(rbtCurso3))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(pnlMatriculaLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(lblTurno)
                    .addComponent(rbtTurnoM)
                    .addComponent(rbtTurnoV)
                    .addComponent(rbtTurnoN)
                    .addComponent(rbtTurnoD))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(pnlMatriculaLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(lblRepetidor)
                    .addComponent(cbxRepetidor))
                .addContainerGap(15, Short.MAX_VALUE))
        );

        pnlMatriculaLayout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {cbxRepetidor, lblCiclo, lblCurso, lblRepetidor, lblTurno, rbtCurso1, rbtCurso2, rbtCurso3, rbtTurnoD, rbtTurnoM, rbtTurnoN, rbtTurnoV});

        pnlAlmacenamiento.setBorder(javax.swing.BorderFactory.createTitledBorder("Almacenamiento"));

        btnCargar.setText("Cargar");
        btnCargar.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnCargarActionPerformed(evt);
            }
        });

        btnGuardar.setText("Guardar");
        btnGuardar.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnGuardarActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout pnlAlmacenamientoLayout = new javax.swing.GroupLayout(pnlAlmacenamiento);
        pnlAlmacenamiento.setLayout(pnlAlmacenamientoLayout);
        pnlAlmacenamientoLayout.setHorizontalGroup(
            pnlAlmacenamientoLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pnlAlmacenamientoLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(btnCargar, javax.swing.GroupLayout.PREFERRED_SIZE, 78, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 98, Short.MAX_VALUE)
                .addComponent(btnGuardar, javax.swing.GroupLayout.PREFERRED_SIZE, 83, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );

        pnlAlmacenamientoLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {btnCargar, btnGuardar});

        pnlAlmacenamientoLayout.setVerticalGroup(
            pnlAlmacenamientoLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(pnlAlmacenamientoLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(pnlAlmacenamientoLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(btnCargar)
                    .addComponent(btnGuardar))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(pnlIdentificacion, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(pnlMatricula, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(pnlAlmacenamiento, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {pnlAlmacenamiento, pnlIdentificacion, pnlMatricula});

        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(pnlIdentificacion, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(pnlMatricula, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(pnlAlmacenamiento, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        bindingGroup.bind();

        pack();
        setLocationRelativeTo(null);
    }// </editor-fold>//GEN-END:initComponents

    private void btnGuardarActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnGuardarActionPerformed
        try {
            if (comprobarValidez()) {
                // Guarda los datos en fichero
                exportarModelo();
            } else {
                JOptionPane.showMessageDialog(this, "Hay datos incorrectos.");
            }
        } catch (Exception ex) {
            JOptionPane.showMessageDialog(this, "Error al guardar los datos.");
        }
    }//GEN-LAST:event_btnGuardarActionPerformed

    private void btnCargarActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCargarActionPerformed
        try {
            // Matricula a partir de los datos del fichero
            importarModelo();

            // Comprobar validez de los datos cargados en el interfaz
            comprobarValidez();
        } catch (Exception ex) {
            JOptionPane.showMessageDialog(this, "Error al cargar los datos");
        }
    }//GEN-LAST:event_btnCargarActionPerformed

    private void txfExpedienteFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txfExpedienteFocusGained
        // Color de la letra en modo edición
        txfExpediente.setForeground(Color.BLACK);

        // Color de fondo de campo seleccionado
        txfExpediente.setBackground(Color.CYAN);
    }//GEN-LAST:event_txfExpedienteFocusGained

    private void txfNombreFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txfNombreFocusGained
        // Color de la letra en modo edición
        txfNombre.setForeground(Color.BLACK);

        // Color de fondo de campo seleccionado
        txfNombre.setBackground(Color.CYAN);
    }//GEN-LAST:event_txfNombreFocusGained

    private void txfApellidosFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txfApellidosFocusGained
        // Color de la letra en modo edición
        txfApellidos.setForeground(Color.BLACK);

        // Color de fondo de campo seleccionado
        txfApellidos.setBackground(Color.CYAN);
    }//GEN-LAST:event_txfApellidosFocusGained

    private void txfNifFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txfNifFocusGained
        // Color de la letra en modo edición
        txfNif.setForeground(Color.BLACK);

        // Color de fondo de campo seleccionado
        txfNif.setBackground(Color.CYAN);
    }//GEN-LAST:event_txfNifFocusGained

    private void txfExpedienteFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txfExpedienteFocusLost
        // Valida el expediente
        expedienteOK = validarCampoTexto(txfExpediente, ER_EXPEDIENTE, "*");

        // Color de fondo de campo seleccionado
        txfExpediente.setBackground(Color.WHITE);
    }//GEN-LAST:event_txfExpedienteFocusLost

    private void txfNombreFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txfNombreFocusLost
        // Valida el nombre
        nombreOK = validarCampoTexto(txfNombre, ER_NOMBRE, "*");

        // Color de fondo de campo seleccionado
        txfNombre.setBackground(Color.WHITE);
    }//GEN-LAST:event_txfNombreFocusLost

    private void txfApellidosFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txfApellidosFocusLost
        // Valida los apellidos
        apellidosOK = validarCampoTexto(txfApellidos, ER_APELLIDOS, "*");

        // Color de fondo de campo seleccionado
        txfApellidos.setBackground(Color.WHITE);
    }//GEN-LAST:event_txfApellidosFocusLost

    private void txfNifFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_txfNifFocusLost
        // Valida el nif
        nifOK = validarCampoTexto(txfNif, ER_NIF, "*");

        // Valida el Nif
        try {
            Nif nif = Nif.parseNif(txfNif.getText());
            txfNif.setText(nif.toString());
            txfNif.setForeground(Color.BLACK);
            nifOK = true;
        } catch (Exception e) {
            txfNif.setForeground(Color.RED);
            nifOK = true;
        }

        // Color de fondo de campo seleccionado
        txfNif.setBackground(Color.WHITE);
    }//GEN-LAST:event_txfNifFocusLost

    public static void main(String args[]) {
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /*
         * If Nimbus (introduced in Java SE 6) is not available, stay with the
         * default look and feel. For details see
         * http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Windows".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(MatriculaEnlazada.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(MatriculaEnlazada.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(MatriculaEnlazada.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(MatriculaEnlazada.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new MatriculaEnlazada().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.ButtonGroup btgCurso;
    private javax.swing.ButtonGroup btgTurno;
    private javax.swing.JButton btnCargar;
    private javax.swing.JButton btnGuardar;
    private javax.swing.JComboBox cbbCiclo;
    private javax.swing.JCheckBox cbxRepetidor;
    private javax.swing.JLabel lblApellidos;
    private javax.swing.JLabel lblCiclo;
    private javax.swing.JLabel lblCurso;
    private javax.swing.JLabel lblExpediente;
    private javax.swing.JLabel lblNif;
    private javax.swing.JLabel lblNombre;
    private javax.swing.JLabel lblRepetidor;
    private javax.swing.JLabel lblTurno;
    private javax.swing.JPanel pnlAlmacenamiento;
    private javax.swing.JPanel pnlIdentificacion;
    private javax.swing.JPanel pnlMatricula;
    private javax.swing.JRadioButton rbtCurso1;
    private javax.swing.JRadioButton rbtCurso2;
    private javax.swing.JRadioButton rbtCurso3;
    private javax.swing.JRadioButton rbtTurnoD;
    private javax.swing.JRadioButton rbtTurnoM;
    private javax.swing.JRadioButton rbtTurnoN;
    private javax.swing.JRadioButton rbtTurnoV;
    private javax.swing.JTextField txfApellidos;
    private javax.swing.JTextField txfExpediente;
    private javax.swing.JTextField txfNif;
    private javax.swing.JTextField txfNombre;
    private org.jdesktop.beansbinding.BindingGroup bindingGroup;
    // End of variables declaration//GEN-END:variables

    private void exportarModelo() throws Exception {
        // Codificador XML de objetos
        XMLEncoder encoder = null;

        // Cauce de salida al fichero XML
        FileOutputStream fos = null;

        try {
            // Cauce binario de salida a fichero
            fos = new FileOutputStream(FICHERO);

            // Codificador de serialización XML
            encoder = new XMLEncoder(fos);

            // Guarda el objeto
            encoder.writeObject(matricula);
        } finally {
            // Cierra el codificador XML
            if (encoder != null) {
                encoder.close();
            }

            // Cierra el acceso de salida al fichero
            if (fos != null) {
                fos.close();
            }
        }
    }

    private void importarModelo() throws Exception {
        // Decodificador XML
        XMLDecoder decoder = null;

        // Cauce de entrada al fichero XML        
        FileInputStream fis = null;

        try {
            // Cauce binario de lectura de un fichero
            fis = new FileInputStream(FICHERO);

            // Decodificador
            decoder = new XMLDecoder(fis);

            // Objeto temporal con los datos leidos
            Matricula matriculaAct = (Matricula) decoder.readObject();

            // Copia masiva de atributos
            BeanUtils.copyProperties(matricula, matriculaAct);
        } finally {
            // Cierra el decodificador XML
            if (decoder != null) {
                decoder.close();
            }

            // Cierra el acceso de entrada al fichero
            if (fis != null) {
                fis.close();
            }
        }
    }

    private boolean validarCampoTexto(JTextField txfActual, String expReg,
            String textoCampoVacio) throws PatternSyntaxException {
        // Semáforo de validación
        boolean validacionOK = false;

        try {
            // Obtiene el texto del campo
            String textoActual = txfActual.getText().trim();

            // Comprueba campo vacio
            if (textoActual.equals("")) {
                textoActual = textoCampoVacio;
            }

            // Restaura el texto formateado
            txfActual.setText(textoActual);

            // Patrón de la expresión regular
            Pattern patron = Pattern.compile(expReg);

            // Aplica el patrón al texto de prueba
            Matcher detector = patron.matcher(textoActual);

            // Comprueba la coincidencia
            if (detector.matches()) {
                // Contenido correcto
                txfActual.setForeground(Color.BLACK);

                // Actualiza el semáforo
                validacionOK = true;
            } else {
                // Contenido problemático
                txfActual.setForeground(Color.RED);
            }
        } catch (PatternSyntaxException pse) {
            // Contenido problemático
            txfActual.setForeground(Color.RED);

            // Expresión regular errónea - Depurar expresión
            JOptionPane.showMessageDialog(this, "Expresión regular errónea");
        }

        // Devuelve el resultado de la validacion
        return validacionOK;
    }

    public Matricula getMatricula() {
        return matricula;
    }

    public void setMatricula(Matricula matricula) {
        this.matricula = matricula;
    }

    private boolean comprobarValidez() {
        // Valida el expediente
        expedienteOK = validarCampoTexto(txfExpediente, ER_EXPEDIENTE, "*");

        // Valida el nombre
        nombreOK = validarCampoTexto(txfNombre, ER_NOMBRE, "*");

        // Valida los apellidos
        apellidosOK = validarCampoTexto(txfApellidos, ER_APELLIDOS, "*");

        // Valida el nif
        nifOK = validarCampoTexto(txfNif, ER_NIF, "*");

        // Valida el Nif
        try {
            Nif nif = Nif.parseNif(txfNif.getText());
            txfNif.setText(nif.toString());
            txfNif.setForeground(Color.BLACK);
            nifOK = true;
        } catch (Exception e) {
            txfNif.setForeground(Color.RED);
            nifOK = true;
        }

        return expedienteOK && nombreOK && apellidosOK && nifOK;
    }

    private void iniciarAnterior() {
        // Genera un modelo predeterminado
        matricula = new Matricula();
    }

    private void iniciarPosterior() {
        // Comprueba la validez de los datos del interfaz
        comprobarValidez();
    }
}
